package rolesystem.examples.jade.auction.auctioneer;

import rolesystem.core.*;
import rolesystem.roles.auction.*;
import rolesystem.jade.agents.*;

import java.io.Serializable;

/**
 * This class represents a possible behaviour of an auctioneer.
 * This is a subject layer that can be used by agents in a role system.
 */
public class AuctioneerSL implements SubjectLayer
{
	/**
	 * First timeout (millisecs).
	 */
	private static final long TO1=3000;

	/**
	 * Second timeout (millisecs).
	 */
	private static final long TO2=10000;

	/**
	 * Third timeout (millisecs).
	 */
	private static final long TO3=3000;

	/**
	 * Milliseconds per ticks of countdown.
	 */
	private static final long MILL_PER_TICKS=1000;

	/**
	 * Relative amount for proposed raises from bid to bid, respect to
	 * current price.
	 */
	private static final double INCREASE=0.1;

	/**
	 * Kind of the auctions of this auctioneer.
	 */
	private static final String MY_KIND="English";

	/**
	 * Registration.
	 */
	private RoleRegistration registration;

	/**
	 * Description of good currently on sale.
	 */
	private String goodDesc;

	/**
	 * Good currently on sale.
	 */
	private Serializable good;

	/**
	 * Seller of good currently on sale.
	 */
	private int seller;

	/**
	 * Price proposed for the good.
	 */
	private Price proposed;

	/**
	 * Price reached by good.
	 */
	private Price reached;

	/**
	 * Bidder currently winning.
	 */
	private int winnBidder;

	/**
	 * State of the finite state machine embedded into this subject.
	 */
	private int state;


	public void go(RoleSystem roleSystem)
	{
		try
		{
			registration=roleSystem.reqRegistration(Auctioneer.ROLE_ID);
		}
		catch(RoleException re)
		{
			System.err.println(re);
			return;
		}
		try
		{
			do
			{
				switch(state)
				{
					case 0:
						handleStateI();
						break;
					case 1:
						handleStateII();
						break;
					case 2:
						handleStateIII();
						break;
					case 3:
						handleStateIV();
						break;
					case 4:
						handleStateV();
						break;
				}
			} while(state<5);
		}
		catch(RoleException re)
		{
			System.err.println(re);
		}
		try
		{
			registration.dismiss();
		}
		catch(RoleException re)
		{
			System.err.println(re);
		}
	}


	/**
	 * Handles the state I of the auction.
	 * There is no auction in progress and the actioneer is waiting for
	 * some seller to request a sale.
	 */
	private void handleStateI() throws RoleException
	{
		RoleEvent event;
		boolean again=true;

		do
		{
			event=registration.listen();
			// A do...while(false) block is used, so a break
			// causes the exit from this block.
			do
			{
				if(Auctioneer.KE_reqSale.match(event))
				{
					again=false;
					break;
				}
				if(Auctioneer.KE_putOnSale.match(event))
				{
					registration.doAction(Auctioneer.
						saleRefused(event.getSender()));
					break;
				}
				if(Auctioneer.KE_askKind.match(event))
				{
					registration.doAction(Auctioneer.
						notifyKind(event.getSender(), MY_KIND));
					break;
				}
				if(Auctioneer.KE_askGood.match(event))
				{
					registration.doAction(Auctioneer.
						auctionOver(event.getSender()));
					break;
				}
				if(Auctioneer.KE_askSituation.match(event))
				{
					registration.doAction(Auctioneer.
						auctionOver(event.getSender()));
					break;
				}
				// KE_bid and KE_pay are ignored.
			} while(false);
		} while(again);
		seller=event.getSender();
		registration.doAction(Auctioneer.
			saleAccepted(seller));
		System.out.println("SaleRequested and accepted");
		state=1;
	}


	/**
	 * Handles the state II of the auction.
	 * There is no auction in progress yet, but a seller has requested a
	 * sale; the actioneer is waiting for the seller to put the good on
	 * sale.
	 */
	private void handleStateII() throws RoleException
	{
		RoleEvent event;
		boolean again=true;
		long residualTime, finalTime;

		finalTime=System.currentTimeMillis()+TO1;
		do
		{
			residualTime=finalTime-System.currentTimeMillis();
			if(residualTime<=0)
			{
				// Timeout TO1 elapsed.
				// Auctioneer restarted.
				state=0;
				return;
			}
			event=registration.listen(residualTime);
			if(event==null)
				continue;
			// A do...while(false) block is used, so a break
			// causes the exit from this block.
			do
			{
				if(Auctioneer.KE_reqSale.match(event))
				{
					registration.doAction(Auctioneer.
						saleRefused(event.getSender()));
					break;
				}
				if(Auctioneer.KE_putOnSale.match(event))
				{
					if(event.getSender()==seller)
						again=false;
					else
						registration.doAction(Auctioneer.
							saleRefused(event.getSender()));
					break;
				}
				if(Auctioneer.KE_askKind.match(event))
				{
					registration.doAction(Auctioneer.
						notifyKind(event.getSender(), MY_KIND));
					break;
				}
				if(Auctioneer.KE_askGood.match(event))
				{
					registration.doAction(Auctioneer.
						auctionOver(event.getSender()));
					break;
				}
				if(Auctioneer.KE_askSituation.match(event))
				{
					registration.doAction(Auctioneer.
						auctionOver(event.getSender()));
					break;
				}
				// KE_bid and KE_pay are ignored.
			} while(false);
		} while(again);
		Sale sale=(Sale)event.getContent();
		good=sale.getGood();
		goodDesc=sale.getGoodDesc();
		proposed=sale.getReserve();
		System.out.println("Good on sale: "+goodDesc);
		System.out.println("The reserve price is: "+proposed);
		state=2;
	}


	/**
	 * Handles the state III of the auction.
	 * The auction is going on and the actioneer is waiting for a first bid
	 * from bidders.
	 */
	private void handleStateIII() throws RoleException
	{
		RoleEvent event;
		boolean again=true;
		long residualTime, finalTime;

		System.out.println("The auction starts now.");
		System.out.println("Who bids for "+proposed+"?");
		finalTime=System.currentTimeMillis()+TO2;
		do
		{
			residualTime=finalTime-System.currentTimeMillis();
			if(residualTime<=0)
			{
				// Timeout TO2 elapsed.
				// Good unsold.
				System.out.println("The good is unsold");
				registration.doAction(Auctioneer.
					unsold(seller));
				state=0;
				return;
			}
			event=registration.listen(residualTime);
			if(event==null)
				continue;
			// A do...while(false) block is used, so a break
			// causes the exit from this block.
			do
			{
				if(Auctioneer.KE_reqSale.match(event))
				{
					registration.doAction(Auctioneer.
						saleRefused(event.getSender()));
					break;
				}
				if(Auctioneer.KE_putOnSale.match(event))
				{
					registration.doAction(Auctioneer.
						saleRefused(event.getSender()));
					break;
				}
				if(Auctioneer.KE_askKind.match(event))
				{
					registration.doAction(Auctioneer.
						notifyKind(event.getSender(), MY_KIND));
					break;
				}
				if(Auctioneer.KE_askGood.match(event))
				{
					registration.doAction(Auctioneer.
						notifyGood(event.getSender(), goodDesc));
					break;
				}
				if(Auctioneer.KE_askSituation.match(event))
				{
					int countdown=(int)
						((residualTime+MILL_PER_TICKS-1)/MILL_PER_TICKS);
					System.out.println("Current countdown: "+countdown);
					registration.doAction(Auctioneer.
						notifySituation(event.getSender(),
						new Situation(proposed, 0, countdown)));
					break;
				}
				if(Auctioneer.KE_bid.match(event))
				{
					Price tmp=(Price)event.getContent();
					if( tmp.getCurrency().equals(proposed.getCurrency()) && 
						tmp.getAmount()>=proposed.getAmount() )
					{
						reached=tmp;
						winnBidder=event.getSender();
						again=false;
					}
					break;
				}
			// KE_pay is ignored.
			} while(false);
		} while(again);
		state=3;
	}



	/**
	 * Handles the state IV of the auction.
	 * The auction is going on and the actioneer is listening to bids from
	 * bidders.
	 */
	private void handleStateIV() throws RoleException
	{
		RoleEvent event;
		long residualTime, finalTime;

		proposed=new Price(reached.getAmount()*(1+INCREASE), reached.getCurrency());
		System.out.println("The good has reached "+reached);
		System.out.println("Who bids for "+proposed+"?");
		finalTime=System.currentTimeMillis()+TO3;
		for(;;)
		{
			residualTime=finalTime-System.currentTimeMillis();
			if(residualTime<=0)
			{
				// Timeout TO3 elapsed.
				// Good sold.
				System.out.println("Final price: "+reached);
				System.out.println("The winner is: "+winnBidder);
				state=4;
				return;
			}
			event=registration.listen(residualTime);
			if(event==null)
				continue;
			// A do...while(false) block is used, so a break
			// causes the exit from this block.
			do
			{
				if(Auctioneer.KE_reqSale.match(event))
				{
					registration.doAction(Auctioneer.
						saleRefused(event.getSender()));
					break;
				}
				if(Auctioneer.KE_putOnSale.match(event))
				{
					registration.doAction(Auctioneer.
						saleRefused(event.getSender()));
					break;
				}
				if(Auctioneer.KE_askKind.match(event))
				{
					registration.doAction(Auctioneer.
						notifyKind(event.getSender(), MY_KIND));
					break;
				}
				if(Auctioneer.KE_askGood.match(event))
				{
					registration.doAction(Auctioneer.
						notifyGood(event.getSender(), goodDesc));
					break;
				}
				if(Auctioneer.KE_askSituation.match(event))
				{
					int countdown=(int)
						((residualTime+MILL_PER_TICKS-1)/MILL_PER_TICKS);
					System.out.println("Current countdown: "+countdown);
					registration.doAction(Auctioneer.
						notifySituation(event.getSender(),
						new Situation(proposed, winnBidder, countdown)));
					break;
				}
				if(Auctioneer.KE_bid.match(event))
				{
					Price tmp=(Price)event.getContent();
					if( tmp.getCurrency().equals(proposed.getCurrency()) && 
						tmp.getAmount()>=proposed.getAmount() )
					{
						reached=tmp;
						winnBidder=event.getSender();
						return;
					}
					break;
				}
			// KE_pay is ignored.
			} while(false);
		}
	}



	/**
	 * Handles the state V of the auction.
	 * The auction is over and the actioneer is waiting for the winning
	 * bidder to pay.
	 */
	private void handleStateV() throws RoleException
	{
		RoleEvent event;
		boolean again=true;

		do
		{
			event=registration.listen();
			// A do...while(false) block is used, so a break
			// causes the exit from this block.
			do
			{
				if(Auctioneer.KE_reqSale.match(event))
				{
					registration.doAction(Auctioneer.
						saleRefused(event.getSender()));
					break;
				}
				if(Auctioneer.KE_putOnSale.match(event))
				{
					registration.doAction(Auctioneer.
						saleRefused(event.getSender()));
					break;
				}
				if(Auctioneer.KE_askKind.match(event))
				{
					registration.doAction(Auctioneer.
						notifyKind(event.getSender(), MY_KIND));
					break;
				}
				if(Auctioneer.KE_askGood.match(event))
				{
					registration.doAction(Auctioneer.
						auctionOver(event.getSender()));
					break;
				}
				if(Auctioneer.KE_askSituation.match(event))
				{
					if( event.getSender()==winnBidder)
						registration.doAction(Auctioneer.
							youWon(event.getSender(), reached));
					else
						registration.doAction(Auctioneer.
							auctionOver(event.getSender()));
					break;
				}
				if(Auctioneer.KE_pay.match(event))
				{
					Price tmp=((Money)event.getContent()).getAmount();
					if( event.getSender()==winnBidder &&
						tmp.getCurrency().equals(reached.getCurrency()) &&
						tmp.getAmount()==reached.getAmount() )
						again=false;
					break;
				}
				// KE_bid is ignored.
			} while(false);
		} while(again);
		registration.doAction(Auctioneer.
			pay(seller, (Money)event.getContent()));
		System.out.println("The winner has paid");
		registration.doAction(Auctioneer.
			giveGood(winnBidder, good));
		state=5;
	}
}
